/*
 * Planificador.c
 *
 *  Created on: 10/10/2011
 *      Author: MadeyMaga
 */

#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/un.h>
#include <errno.h>
#include <sys/epoll.h>
#include <semaphore.h>
#include <errno.h>
#include "Planificador.h"
#include "nipc.h"
#include "config.h"
#include "Consola.h"
#include "Disco.h"
#include "busqueda.h"
#include "raid.h"
#include "fs.h"

//t_log 				*s_log;
struct t_pedido 	*lista1, *lista2;
int 				cantidadDePedidos1 = 0, cantidadDePedidos2 = 0, modoinit;

int					sectors,
					totalSectors,
					algoritmo,
					pistaActual,
					sectorActual,
					discoid,
					maxfs, /* cantidad máxima de conexiones con el fs */
					cantidadSock = 0; /* cantidad de sockets en el poll */
unsigned int		retardo;
struct t_sockets	*tablaSock = NULL;
int				fdPoll;
sem_t			semPedidos;

int main ()
 {
	char 		logflag[8];
	pthread_t	idPedido;

	memset(logflag, 0, 8);

	cargarConfig("PPD.config");
	getConfig("logflag", &logflag);

//	crearLog(logflag);

	cargarInfoBusqueda();

	atexit(&clearConfig);
	atexit(&cerrarSockets);
	atexit(&desmapear);
	atexit(&clearTablaSock);

	if(mapDisk())
	{
	    perror("No se pudo mapear el archivo.\n");
	    return EXIT_FAILURE;
	}

	initColasPedidos();

	pthread_create(&idPedido, NULL, hiloPedidos, NULL);

	fdPoll = crearPoll();

	atenderPoll();

//	log_destroy(s_log);

	return 0;
}

int crearLog(char *logflag){

	int 	flag[] = {0,0,0,0}, i = 0, cantidadDeFlags = 0, logconsola = 0;
	char 	*tok;

	getConfig("logconsola", &logconsola);

	tok = strtok(logflag, "|");

	do
	{
		flag[i] = atoi(tok);
		tok = strtok(NULL, "|");
		cantidadDeFlags++;
		i++;
	}while(tok != NULL);

/*	switch(cantidadDeFlags)
	{
		case 1:
			s_log = log_create("Planificador", "log_plani.txt", flag[0], logconsola);
			break;
		case 2:
			s_log = log_create("Planificador", "log_plani.txt", flag[0] | flag[1], logconsola);
			break;
		case 3:
			s_log = log_create("Planificador", "log_plani.txt", flag[0] | flag[1] | flag[2], logconsola);
			break;
		case 4:
			s_log = log_create("Planificador", "log_plani.txt", flag[0] | flag[1] | flag[2] | flag[3], logconsola);
			break;
	}
*/
	return 0;
}

int crearSockConsola()
{
	int 				sock, retval;
	char				pathSockUnix[14];
	struct sockaddr_un 	dir;
	nipcMessage 		msg = nipc_initmsg;

	dir.sun_family = AF_UNIX;

	getConfig("pathsockunix", &pathSockUnix);

	memset(dir.sun_path, '\0',108 );
	strcpy(dir.sun_path,pathSockUnix);

	sock = socket(AF_UNIX, SOCK_STREAM, 0);

	if(sock == -1)
	{
		printf("Error al crear el socket. Error n°: %d, descripcion: %s.\n", errno, strerror(errno));
		return 1;
	}

	if(bind(sock, (struct sockaddr*)&dir, sizeof(dir)) == -1)
	{
		if(errno == EADDRINUSE)
		{
			system("rm /tmp/PPD.temp");
			printf("Error system: %d,%s\n.", errno, strerror(errno));
			if(0 > bind(sock, (struct sockaddr*)&dir, sizeof(dir)))
			{
				printf("Imposible bindear el socket para la consola. Error n°: %d, descripcion: %s.\n", errno, strerror(errno));
				close(sock);
				return(-1);
			}
		} else {
			printf("Imposible bindear el socket para la consola. Error n°: %d, descripcion: %s.\n", errno, strerror(errno));
			close(sock);
			return(-1);
		}
	}

	listen(sock, 1);

	abrirConsola();

	retval = nipc_recv(&msg, sock);

	if ((retval == NIPC_ERROR) || (msg.payloadLength != 0))
	{
		perror("Handshake consola: ");
		msg.type = '-';
		nipc_send(&msg, sock);
		close(sock);
		return(-1);

	} else {

		/* el handshake esta bien */
		memset(msg.payloadID, 0, 6);
		sprintf(msg.payloadID, "%d", NIPC_ERROR_NONE);
		/* Mandamos la respuesta del handshake */
		nipc_send(&msg, sock);
	}

	return sock;
}

int abrirConsola(){

	pid_t 	p_id;
	char 	*cmd[] = {"ConsolaPPD", NULL};

	p_id = fork();

	if(p_id == 0)
		execv(FILEPATHCONSOLA, cmd);

	return 0;
}

int crearPoll()
{
	int	fdConsola, fdRaid, fdFs;

   	getConfig("maxfs", &maxfs);

	fdPoll = epoll_create(maxfs + 2);

	if (fdPoll == -1)
	{
	  printf("Error al crear el poll. Error n°: %d, descripcion: %s.\n", errno, strerror(errno));
	  exit(1);
	}

	/*	fdConsola = crearSockConsola();

	if(fdConsola != -1)
	{
		pollIn(fdConsola);
		agregarFdEnTablaSock(fdConsola, P_CONSOLA);
	}
*/
	getConfig("modoinit", &modoinit);

	if(modoinit == 1)
	{
		fdRaid = crearSockRaid();
		if(fdRaid != -1)
		{
			pollIn(fdRaid);
			agregarFdEnTablaSock(fdRaid, P_RAID);
			printf("Poll creado con exito!\n");
			return fdPoll;
		}
	} else {
		fdFs = crearSockFsListener();
		if(fdFs != -1)
		{
			pollIn(fdFs);
			agregarFdEnTablaSock(fdFs, P_FS_LISTENER);
			printf("Poll creado con exito!\n");
			return fdPoll;
		}
	}

	printf("Error al crear el poll!\n");

	exit(0);
}

void pollIn(int fd)
{
	struct 	epoll_event event;
	int 	s;

	memset(&event, 0, sizeof(event));

	event.data.fd = fd;
	event.events = EPOLLIN;
	s = epoll_ctl(fdPoll, EPOLL_CTL_ADD, fd, &event);

	if (s == -1)
	{
	  printf("Error al agregar un fd. Error n°: %d, descripcion: %s.\n", errno, strerror(errno));
	  exit(1);
	}
}

void atenderPoll()
{
	struct 	epoll_event 	*events;
	int						cantWait = -1, i, j = 0, posicion, maxEvents = maxfs + 2;
	struct t_sockets		*nodoSocket;

 	events = calloc(maxEvents, sizeof(struct epoll_event));

 	if(events == NULL)
 	{
 		printf("Error en el calloc. N° %d, descripcion: %s\n", errno, strerror(errno));
 		exit(0);
 	}

	do{
		cantWait = epoll_wait(fdPoll, events, maxEvents, -1);

		if(cantWait == -1)
		{
			if(errno == EINTR)
			{

			}
			else
			{
				printf("Error en el epoll_wait. N° %d, descripcion: %s\n", errno, strerror(errno));
				exit(0);
			}
		}

		for (i = 0; i < cantWait; i++)
		{
			nodoSocket = tablaSock;

			while(nodoSocket != NULL)
			{
				if(nodoSocket->fd != events[i].data.fd)
				{
					nodoSocket = nodoSocket->siguiente;
				}
			}

			switch(nodoSocket->proceso)
			{
				case P_CONSOLA:
					atenderConsola(nodoSocket->fd);
					break;
				case P_RAID:
					atenderRaid(nodoSocket->fd);
					break;
				case P_FS_LISTENER:
					atenderFsListener(fdPoll, nodoSocket->fd);
					break;
				case P_FS:
					atenderFs(nodoSocket->fd);
					break;
				default:
					printf("Se salio por default en el wait.\n");
					exit(1);
					break;
			}
		}
	} while((cantWait > 0) || (errno == EINTR));

	free(events);
}

void agregarFdEnTablaSock(int fdCon, int tipoConexion)
{
	struct t_sockets	*regInsertar;

	regInsertar = (struct t_sockets*) malloc (sizeof(struct t_sockets));

	regInsertar->fd = fdCon;
	regInsertar->proceso = tipoConexion;
	regInsertar->siguiente = tablaSock;

	tablaSock = regInsertar;
}

void clearTablaSock()
{
	struct t_sockets *aux;

	while(tablaSock != NULL)
	{
		aux = tablaSock;
		tablaSock = aux->siguiente;

		free(aux);
	}

	free(lista1);
	free(lista2);
}

void cerrarSockets()
{
	int i;

	if(tablaSock != NULL)
	{
		/* conexion con la consola */
		close(tablaSock[0].fd);
		system("rm /tmp/PPD.temp");

		for(i = 1; i < cantidadSock; i++)
		{
			close(tablaSock[i].fd);
		}

		close(fdPoll);
	}
}

void initColasPedidos(){

	if (sem_init(&semPedidos, 0, 1) < 0)
	{
		perror("sem_init");
	}

	lista1 = NULL;
	lista2 = NULL;
}
